#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// Noeud
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
bool Noeud::getError() {
  return error;
}

////////////////////////////////////////////////////////////////////////////////
void Noeud::setError(bool err) {
  this->error=err;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
  this->error=false;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
vector<Noeud *> NoeudSeqInst::getTabInst() {
  return this->tabInst;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation 
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;


  if (expression->getError()==true) {
    this->error=true;
  }
  else {
    this->error=false;  
  }

}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;

  if ((operandeGauche->getError()==true) || (operandeDroit->getError()==true)) {
    this->setError(true);
  }
  else {
    this->setError(false);   
  }
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();

  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od;
  else if (this->operateur=="et") valeur = og*od;
  else if (this->operateur=="ou") valeur = og+od;
  else if (this->operateur=="<") valeur = (og<od);
  else if (this->operateur==">") valeur = (og>od);
  else if (this->operateur==">=") valeur = (og>=od);
  else if (this->operateur=="<=") valeur = (og<=od);
  else if (this->operateur=="==") valeur = (og==od);
  else if (this->operateur=="!=") valeur = (og!=od);

  else  // this->operateur=="/"
  if (od!=0)
    valeur = og/od;
    else {
      cout << "Erreur pendant l'interpretation : division par zero" << endl;
      exit(0); // plus tard on levera une exception 
    }
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////
NoeudInstSi::NoeudInstSi(vector<Noeud *> tabCond,vector<Noeud *> tabInst) {
        this->tabCond=tabCond;
        this->tabInst=tabInst;
        this->error=false;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::ajouteCond(Noeud* cond) {

	tabCond.push_back(cond);
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::ajouteSeq(Noeud* seq) {
	tabInst.push_back(seq);
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstSi::getValeur() {
	unsigned int i=0;
	int valeur=0;
  
	while (i<tabCond.size() && tabCond[i]!=NULL && !tabCond[i]->getValeur()) {
		i++;
	}
	if (i<=tabCond.size())
	{
		tabInst[i]->getValeur();	
	}

	return valeur;	
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);

  cout << "Noeud - Opération conditionnelle - " << tabInst.size() << " instruction(s) " << endl;
  for (unsigned int i=0; i<tabCond.size(); i++)
  {    
	tabCond[i]->afficher(indentation+1); 
	tabInst[i]->afficher(indentation+1);
	
  }
}

////////////////////////////////////////////////////////////////////////////////
// NoeudBoucle
////////////////////////////////////////////////////////////////////////////////
NoeudBoucle::NoeudBoucle(Symbole tag,Noeud* condition, Noeud* seqInst){
  this->tag=tag;
  this->condition=condition;
  this->seqInst=seqInst;
  
  if ((condition->getError()==true) || (seqInst->getError()==true)) {
    this->error=true;
  }
  else {
    this->error=false;    
  }
}

////////////////////////////////////////////////////////////////////////////////
int NoeudBoucle::getValeur() {
  int valeur=0;

  if (tag.getChaine()=="tantque" || tag.getChaine()=="pour")
  {
    while (condition->getValeur()==1)
    {
      valeur=valeur+seqInst->getValeur();
    }
  }
  else //(tag.getChaine()=="repeter")
  {
    do 
      {
        valeur=valeur+seqInst->getValeur();
      }while (condition->getValeur()==0);

  }
  return valeur;
}
////////////////////////////////////////////////////////////////////////////////
void NoeudBoucle::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);  
  cout << "Noeud - Boucle" << endl;
  condition->afficher(indentation+1);
  seqInst->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudPour
////////////////////////////////////////////////////////////////////////////////
NoeudPour::NoeudPour(Symbole tag,Noeud* condition, Noeud* seqInst,Noeud* affectInit,Noeud* affectIt) : NoeudBoucle(tag,condition,seqInst){
  this->affectInit=affectInit;
  this->affectIt=affectIt;

  if ((affectInit->getError()==true) || (affectIt->getError()==true)) {
    this->error=true;
  }
  else {
    this->error=false;    
  }
}
////////////////////////////////////////////////////////////////////////////////
int NoeudPour::getValeur() {
  int valeur=0,val=0;
  valeur=affectInit->getValeur();
  while (condition->getValeur()==1)
  {
    valeur=valeur+affectIt->getValeur();
    val=val+seqInst->getValeur();
  }
  //NoeudPour::getValeur();
  return val;

}
////////////////////////////////////////////////////////////////////////////////
// NoeudIO
////////////////////////////////////////////////////////////////////////////////
NoeudIO::NoeudIO(Symbole sym, Noeud* variable) {
  this->sym=sym;
  this->variable=variable;
  this->setError(false);

}
////////////////////////////////////////////////////////////////////////////////
int NoeudIO::getValeur() {

int valeur=0;

if (sym.getChaine()=="lire")
{	int v;
	cin >> v;	
	((SymboleValue*)variable)->setValeur(v);
}
else if (sym.getChaine()=="ecrire")
{
	cout<< variable->getValeur();
}
  return valeur; // par convention, une affectation a pour valeur la valeur affectée

}

////////////////////////////////////////////////////////////////////////////////
void NoeudIO::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->sym.getChaine() << "\" applique a : " << endl;
  variable->afficher(indentation+1);  // on affiche fils gauche et fils droit
}

////////////////////////////////////////////////////////////////////////////////
// NoeudError
////////////////////////////////////////////////////////////////////////////////
NoeudError::NoeudError() {
  this->setError(true);
}
////////////////////////////////////////////////////////////////////////////////
int NoeudError::getValeur() {
  return 0;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudError::afficher() {
}
